home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 February: Tool Chest / Apple_Developer_Group_CD_Series_February_1998_Tool_Chest.iso / Sample Code / Sound PreMixer effect / VU-Meter.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-06-18  |  13.8 KB  |  370 lines  |  [TEXT/CWIE]

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    Sample pre-mixer sound component 
  5. **
  6. **    by Mark Cookson, Apple Developer Technical Support
  7. **
  8. **    File:    VU-Meter.c
  9. **
  10. **    Copyright ©1996 Apple Computer, Inc.
  11. **    All rights reserved.
  12. **
  13. **    You may incorporate this sample code into your applications without
  14. **    restriction, though the sample code has been provided "AS IS" and the
  15. **    responsibility for its operation is 100% yours.  However, what you are
  16. **    not permitted to do is to redistribute the source as "Apple Sample
  17. **    Code" after having made changes. If you're going to re-distribute the
  18. **    source, we require that you make it clear in the source that the code
  19. **    was descended from Apple Sample Code, but that you've made changes.
  20. */
  21.  
  22. #include <Memory.h>
  23. #include <Errors.h>
  24. #include <SoundInput.h>
  25. #include <Components.h>
  26. #include <Gestalt.h>
  27. #include <Sound.h>
  28.  
  29. #include "VU-Meter.h"
  30.  
  31.  
  32. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  33. // Constants
  34. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  35.  
  36. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  37. // Data Structures
  38. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  39.  
  40. /* Sound component globals */
  41.  
  42. typedef struct {
  43. // these are general purpose variables that every sound component will need
  44.     ComponentInstance        sourceComponent;            // component to call when we need more data
  45.     Handle                    globalsHandle;                // handle to component globals
  46.     short                    peakSampleLeft;
  47.     short                    peakSampleRight;
  48. } SoundComponentGlobals, *SoundComponentGlobalsPtr;
  49.  
  50.  
  51. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  52. //            Component Dispatcher
  53. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  54.  
  55. #define SoundComponentEntryPoint        main    
  56.  
  57. #include "ComponentDispatch.c"
  58.  
  59.  
  60. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  61. //            prototypes
  62. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  63.  
  64. static void VUMeterBuffer(SoundComponentGlobalsPtr globals, const Byte * const inputBuffer, long samples, short sampleSize, short numChannels);
  65.  
  66.  
  67. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  68. // Component Manager Methods
  69. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  70. /*    ==============================================================================
  71.     Component Open
  72.  
  73.     This routine is called when the Component Manager creates an instance of this
  74.     component. The routine should allocate global variables in the appropriate heap
  75.     and call SetComponentInstanceStorage() so the Component Manager can remember
  76.     the globals and pass them to all the method calls.
  77.     
  78.     Determining the heap to use can be tricky. The Component Manager will normally
  79.     load the component code into the system heap, which is good, since many applications
  80.     will be sharing this component to play sound. In this case, the components's global
  81.     variable storage should also be created in the system heap.
  82.  
  83.     However, if system heap memory is tight, the Component Manager will load
  84.     the component into the application heap of the first application that plays sound.
  85.     When this happens, the component should create global storage in the application heap
  86.     instead. The Sound Manager will make sure that other applications will not try
  87.     to play sound while the component is in this application heap.
  88.  
  89.     To determine the proper heap to use, call GetComponentInstanceA5(). If the value
  90.     returned is 0, then the component was loaded into the system heap, and all storage
  91.     should be allocated there. If the value returned is non-zero, the component is in
  92.     the application heap specifed by returned A5 value, and all storage should be
  93.     allocated in this application heap.
  94.     
  95.     NOTE: If the component is loaded into the application heap, the value returned by
  96.     GetComponentRefCon() will be 0.
  97.     NOTE: Do not attempt to initialize in this call, since the Component Manager will
  98.     call Open() BEFORE calling Register().
  99.     NOTE: This routine is never called at interrupt time.
  100.     ============================================================================== */
  101.  
  102. static pascal ComponentResult __SoundComponentOpen(void *unused1, ComponentInstance self) {
  103. #pragma unused (unused1)
  104.  
  105.     Handle            h;
  106.     SoundComponentGlobalsPtr        globals;
  107.  
  108.     h = NewHandleClear(sizeof(SoundComponentGlobals));        // get space for globals
  109.     if (h == nil)
  110.         return(MemError());
  111.  
  112.     HLock(h);
  113.     globals = (SoundComponentGlobalsPtr) *h;
  114.     SetComponentInstanceStorage (self, (Handle) globals);     // save pointer to our globals
  115.  
  116.     globals->globalsHandle = h;                                // remember the handle
  117.  
  118.     return (noErr);
  119. }
  120.  
  121. /*    ==============================================================================
  122.     Component Close
  123.  
  124.     This routine is called when the Component Manager is closing the instance of
  125.     this component. It should delete all global storage and close any other components
  126.     that were opened.
  127.     
  128.     NOTE: Be sure to check that the globals pointer passed in to this routine is
  129.     not set to NIL. If the Open() routine fails for any reason, the Component
  130.     Manager will call this routine passing in a NIL for the globals.
  131.     NOTE: This routine is never called at interrupt time.
  132.     ============================================================================== */
  133.  
  134. static pascal ComponentResult __SoundComponentClose(SoundComponentGlobalsPtr globals, ComponentInstance self) {
  135. #pragma unused (self)
  136.  
  137.     if (globals) {                                            // we have some globals
  138.         if (globals->sourceComponent)                        // we have a source component
  139.             CloseComponent(globals->sourceComponent);        // close it
  140.  
  141.         DisposeHandle(globals->globalsHandle);                // dispose our storage
  142.     }
  143.  
  144.     return (noErr);
  145. }
  146.  
  147. /*    ==============================================================================
  148.     Component Register
  149.  
  150.     This routine is called once, usually at boot time, when the Component Manager
  151.     is first registering this sound component. This routine should check to see if the proper
  152.     Sound Manager is installed and return 0 if it is. If the right Sound Manager is not
  153.     installed, the routine should return 1 and this component will not be registered.
  154.  
  155.     NOTE: The cmpWantsRegisterMessage bit must be set in the component flags of the
  156.     sound component in order for this routine to be called.
  157.     NOTE: This routine is never called at interrupt time.
  158.     ============================================================================== */
  159.  
  160. static pascal ComponentResult __SoundComponentRegister(SoundComponentGlobalsPtr globals)
  161. {
  162. #pragma unused (globals)
  163.  
  164.     NumVersionVariant    version;
  165.  
  166.     version.parts = SndSoundManagerVersion();                    // get the Sound Manager version
  167.     if (version.whole < 0x03210000)                                // it's what we need
  168.         return (1);                                                // do not install component
  169.     else
  170.         return (0);                                                // install this component
  171. }
  172.  
  173. /*    ==============================================================================
  174.     Component GetInfo
  175.  
  176.     This is called when a program issues a SndGetInfo() call.  If we see our selector
  177.     we return the peak value of the last buffer, if it's anything else we forward
  178.     the selector to our source component.
  179.     ============================================================================== */
  180.  
  181. static pascal ComponentResult __SoundComponentGetInfo(SoundComponentGlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr)
  182. {
  183.     ComponentResult        result = noErr;
  184.  
  185.     switch (selector) {
  186.     
  187.         case kVUSelectorSubType:
  188.             ((short*)infoPtr)[0] = globals->peakSampleLeft;
  189.             ((short*)infoPtr)[1] = globals->peakSampleRight;
  190.             break;
  191.  
  192.         default:
  193.             result = SoundComponentGetInfo(globals->sourceComponent, sourceID, selector, infoPtr);
  194.             break;
  195.     }
  196.  
  197.     return (result);
  198. }
  199.  
  200. /*    ==============================================================================
  201.     StopSource
  202.  
  203.     This routine is used to stop sounds that are currently playing. It should
  204.     clear out any internal buffers, reset any compression state information
  205.     and then delegate the call up the chain.  We have no state so we just send
  206.     the call up the chain.
  207.  
  208.     NOTE: This can be called at interrupt time.
  209.     ============================================================================== */
  210.  
  211. static pascal ComponentResult __SoundComponentStopSource(SoundComponentGlobalsPtr globals, short count, SoundSource *sources) {
  212.  
  213.     // values are no longer valid
  214.     globals->peakSampleLeft = 0;
  215.     globals->peakSampleRight = 0;
  216.  
  217.     // delegate this call
  218.     return (SoundComponentStopSource(globals->sourceComponent, count, sources));
  219. }
  220.  
  221. /*    ==============================================================================
  222.     PlaySourceBuffer
  223.  
  224.     This routine is used to start a new sound playing. It should clear out any internal buffers
  225.     but should NOT reset any compression state information, since this could be a
  226.     continuation of a sound that has been broken into pieces. Then the call should be
  227.     delegated up the chain.  We have no state so we just send the call up the chain.
  228.  
  229.     NOTE: This can be called at interrupt time.
  230.     ============================================================================== */
  231.  
  232. static pascal ComponentResult __SoundComponentPlaySourceBuffer(SoundComponentGlobalsPtr globals, SoundSource sourceID, SoundParamBlockPtr pb, long actions) {
  233.  
  234.     // values are no longer valid, starting a new sound
  235.     globals->peakSampleLeft = 0;
  236.     globals->peakSampleRight = 0;
  237.  
  238.     // delegate this call
  239.     return (SoundComponentPlaySourceBuffer(globals->sourceComponent, sourceID, pb, actions));
  240. }
  241.  
  242. /*    ==============================================================================
  243.     SetSource
  244.  
  245.     This routine sets the component we should call to get more data. We must remember
  246.     this component.
  247.     ============================================================================== */
  248.  
  249. static pascal ComponentResult __SoundComponentSetSource(SoundComponentGlobalsPtr globals, SoundSource sourceID, ComponentInstance source) {
  250. #pragma unused (sourceID)
  251.  
  252.     // remember our source component
  253.     globals->sourceComponent = source;
  254.  
  255.     return (noErr);
  256. }
  257.  
  258. /*    ==============================================================================
  259.     GetSource
  260.  
  261.     This routine returns the component we call to get more data.
  262.     ============================================================================== */
  263.  
  264. static pascal ComponentResult __SoundComponentGetSource(SoundComponentGlobalsPtr globals, SoundSource sourceID, ComponentInstance *source) {
  265. #pragma unused (sourceID)
  266.  
  267.     *source = globals->sourceComponent;
  268.     return (noErr);
  269. }
  270.  
  271. /*    ==============================================================================
  272.     SetOutput
  273.  
  274.     This routine sets the data format our component should output. If we can't output
  275.     the requested format, we should return a pointer to the format we do support,
  276.     and return an error, and the Sound Manager will attempt the conversion for us.
  277.  
  278.     For this component, because we don't modify the data there is no problem
  279.     outputting any format.
  280.     ============================================================================== */
  281.  
  282. static pascal ComponentResult __SoundComponentSetOutput(SoundComponentGlobalsPtr globals, SoundComponentDataPtr requested, SoundComponentDataPtr *actual) {
  283. #pragma unused (actual, globals, requested)
  284.  
  285.     // no problem outputting anything because we only "look" at the data
  286.     return (noErr);
  287. }
  288.  
  289. /*    ==============================================================================
  290.     GetSourceData
  291.  
  292.     This routine is called when the Sound Manager wants your component to process
  293.     some more data.
  294.  
  295.     NOTE: This will most often be called at interrupt time.
  296.     ============================================================================== */
  297.  
  298. static pascal ComponentResult __SoundComponentGetSourceData(SoundComponentGlobalsPtr globals, SoundComponentDataPtr *resultDataPtr) {
  299. #pragma unused (resultDataPtr)
  300.     ComponentResult            result;
  301.  
  302.     // Get some sound data to look at
  303.     result = SoundComponentGetSourceData(globals->sourceComponent, resultDataPtr);
  304.  
  305.     // Sample the buffer to get VU meter data
  306.     VUMeterBuffer(globals, (*resultDataPtr)->buffer, (*resultDataPtr)->sampleCount, (*resultDataPtr)->sampleSize, (*resultDataPtr)->numChannels);
  307.  
  308.     return result;
  309. }
  310.  
  311. /*    ==============================================================================
  312.     VUMeterBuffer
  313.  
  314.     This implements a simple peak meter.
  315.  
  316.     This code works on the assumtion that the average of a sound wave is 0 (for
  317.     each positive sample there is a coresponding negative sample), therefore we
  318.     only have to find the largest positive sample in the buffer because that will
  319.     be very close to the absolute value of the largest negative sample.
  320.     ============================================================================== */
  321.  
  322. static void VUMeterBuffer(SoundComponentGlobalsPtr globals, const Byte * const inputBuffer, long samples, short sampleSize, short numChannels) {
  323.     int        i;
  324.     short    *shortBuffer;
  325.     Byte    *charBuffer;
  326.  
  327.     shortBuffer = (short*)inputBuffer;
  328.     charBuffer = (Byte*)inputBuffer;
  329.  
  330.     globals->peakSampleLeft = 0;
  331.     globals->peakSampleRight = 0;
  332.  
  333.     if (sampleSize == 8) {
  334.         if (numChannels == 1) {
  335.             for (i = 0; i < samples; i++) {
  336.                 if (*charBuffer > globals->peakSampleLeft)
  337.                     globals->peakSampleLeft = *charBuffer;
  338.                 charBuffer++;
  339.             }
  340.         } else if (numChannels == 2) {
  341.             for (i = 0; i < samples * 2; i++) {
  342.                 if (*charBuffer > globals->peakSampleLeft)
  343.                     globals->peakSampleLeft = *charBuffer;
  344.                 charBuffer++;
  345.                 if (*charBuffer > globals->peakSampleRight)
  346.                     globals->peakSampleRight = *charBuffer;
  347.                 charBuffer++;
  348.             }
  349.         }
  350.     } else if (sampleSize == 16) {
  351.         if (numChannels == 1) {
  352.             for (i = 0; i < samples; i++) {
  353.                 if (*shortBuffer > globals->peakSampleLeft)
  354.                     globals->peakSampleLeft = *shortBuffer;
  355.                 shortBuffer++;
  356.             }
  357.         } else if (numChannels == 2) {
  358.             for (i = 0; i < samples * 2; i++) {
  359.                 if (*shortBuffer > globals->peakSampleLeft)
  360.                     globals->peakSampleLeft = *shortBuffer;
  361.                 shortBuffer++;
  362.                 if (*shortBuffer > globals->peakSampleRight)
  363.                     globals->peakSampleRight = *shortBuffer;
  364.                 shortBuffer++;
  365.             }
  366.         }
  367.     }
  368. }
  369.  
  370.